package cn.ulegal.xudy.Security;

import cn.ulegal.xudy.Service.serviceimpl.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.vote.AbstractAccessDecisionManager;
import org.springframework.security.access.vote.AffirmativeBased;
import org.springframework.security.access.vote.AuthenticatedVoter;
import org.springframework.security.access.vote.RoleVoter;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy;
import org.springframework.security.web.session.ConcurrentSessionFilter;
import org.springframework.security.web.session.HttpSessionEventPublisher;
import org.springframework.security.web.session.SessionInformationExpiredStrategy;
import org.springframework.security.web.session.SimpleRedirectSessionInformationExpiredStrategy;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
/**
 * Created by ZhenWeiLai on on 2016-10-16.
 * <p>
 * 三种方法级权限控制
 * <p>
 * 1.securedEnabled: Spring Security’s native annotation
 * 2.jsr250Enabled: standards-based and allow simple role-based constraints
 * 3.prePostEnabled: expression-based
 */
@EnableWebSecurity
//@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

//        @Resource
//        private UserDetailsService userDetailsService;

        @Autowired
        UserDetailsServiceImpl userDetailsService;

//        @Resource
//        private FilterInvocationSecurityMetadataSource securityMetadataSource;

        @Autowired
        FilterSecurityMetadataSource securityMetadataSource;

        @Resource
        private SessionRegistry sessionRegistry;

        @Override
        public void configure(WebSecurity web) throws Exception {
            web.ignoring().antMatchers("/assets/**");
            web.ignoring().antMatchers("/components/**");
            web.ignoring().antMatchers("/css/**");
            web.ignoring().antMatchers("/images/**");
            web.ignoring().antMatchers("/js/**");
            web.ignoring().antMatchers("/mustache/**");
            web.ignoring().antMatchers("/favicon.ico");

            //注册地址不拦截
//        web.ignoring().antMatchers("/base/invoice/userinfo/u/reg");

//        web.ignoring().antMatchers("/**");
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            //解决不允许显示在iframe的问题
            http.headers().frameOptions().disable();

            http.addFilterAt(usernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
            //session并发控制过滤器
            http.addFilterAt(new ConcurrentSessionFilter(sessionRegistry,sessionInformationExpiredStrategy()),ConcurrentSessionFilter.class);
            //自定义过滤器
            //在适当的地方加入
            http.addFilterAt(filterSecurityInterceptor(securityMetadataSource,
                    accessDecisionManager(), authenticationManagerBean()), FilterSecurityInterceptor.class);

            http.exceptionHandling()
                    .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"))
                    .and().logout().logoutUrl("/logout").logoutSuccessUrl("/login").permitAll().and()
                    .exceptionHandling().accessDeniedPage("/accessDenied");


            http.authorizeRequests().anyRequest().fullyAuthenticated();

            // 关闭csrf
            http.csrf().disable();


            /**
             * 以下配置无效
             */
        //session管理
        //session失效后跳转
//        http.sessionManagement().invalidSessionUrl("/login");
        //只允许一个用户登录,如果同一个账户两次登录,那么第一个账户将被踢下线,跳转到登录页面
//        http.sessionManagement().maximumSessions(1).expiredUrl("/login");

        }


        @Override
        protected void configure(AuthenticationManagerBuilder auth)
                throws Exception {
            // 自定义UserDetailsService,设置加密算法
            auth.userDetailsService(userDetailsService);
//                    .passwordEncoder(passwordEncoder());
            //不删除凭据，以便记住用户
            auth.eraseCredentials(false);
        }

        //session失效跳转
        private SessionInformationExpiredStrategy sessionInformationExpiredStrategy() {
            return new SimpleRedirectSessionInformationExpiredStrategy("/login");
        }

        @Bean
        public SessionRegistry sessionRegistry() {
            return new SessionRegistryImpl();
        }

        //SpringSecurity内置的session监听器
        @Bean
        public HttpSessionEventPublisher httpSessionEventPublisher() {
            return new HttpSessionEventPublisher();
        }

        private UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter() throws Exception {
            UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter = new CuzUsernamePasswordAuthenticationFilter();
            usernamePasswordAuthenticationFilter.setPostOnly(true);
            usernamePasswordAuthenticationFilter.setAuthenticationManager(this.authenticationManager());
            usernamePasswordAuthenticationFilter.setUsernameParameter("name_key");
            usernamePasswordAuthenticationFilter.setPasswordParameter("pwd_key");
            usernamePasswordAuthenticationFilter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/checkLogin", "POST"));
            usernamePasswordAuthenticationFilter.setAuthenticationFailureHandler(simpleUrlAuthenticationFailureHandler());
            usernamePasswordAuthenticationFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler());
            //session并发控制,因为默认的并发控制方法是空方法.这里必须自己配置一个
            usernamePasswordAuthenticationFilter.setSessionAuthenticationStrategy(new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry));
            return usernamePasswordAuthenticationFilter;
        }

//    @Bean
//    public LoggerListener loggerListener() {
//        System.out.println("org.springframework.security.authentication.event.LoggerListener");
//        return new LoggerListener();
//    }
//
//    @Bean
//    public org.springframework.security.access.event.LoggerListener eventLoggerListener() {
//        System.out.println("org.springframework.security.access.event.LoggerListener");
//        return new org.springframework.security.access.event.LoggerListener();
//    }

        /**
         * 投票器
         */
        private AbstractAccessDecisionManager accessDecisionManager() {
            List<AccessDecisionVoter<? extends Object>> decisionVoters = new ArrayList();
            decisionVoters.add(new AuthenticatedVoter());
            decisionVoters.add(new RoleVoter());//角色投票器,默认前缀为ROLE_
            RoleVoter AuthVoter = new RoleVoter();
            AuthVoter.setRolePrefix("AUTH_");//特殊权限投票器,修改前缀为AUTH_
            decisionVoters.add(AuthVoter);
            AbstractAccessDecisionManager accessDecisionManager = new AffirmativeBased(decisionVoters);
            return accessDecisionManager;
        }

        @Override
        public AuthenticationManager authenticationManagerBean() {
            AuthenticationManager authenticationManager = null;
            try {
                authenticationManager = super.authenticationManagerBean();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return authenticationManager;
        }


        /**
         * 验证异常处理器
         *
         * @return
         */
        private SimpleUrlAuthenticationFailureHandler simpleUrlAuthenticationFailureHandler() {
            System.out.println("验证异常处理器");
            return new SimpleUrlAuthenticationFailureHandler("/getLoginError");
        }


//    /**
//     * 表达式控制器
//     *
//     * @return
//     */
//    private DefaultWebSecurityExpressionHandler webSecurityExpressionHandler() {
//        DefaultWebSecurityExpressionHandler webSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler();
//        return webSecurityExpressionHandler;
//    }

//    /**
//     * 表达式投票器
//     *
//     * @return
//     */
//    private WebExpressionVoter webExpressionVoter() {
//        WebExpressionVoter webExpressionVoter = new WebExpressionVoter();
//        webExpressionVoter.setExpressionHandler(webSecurityExpressionHandler());
//        return webExpressionVoter;
//    }

        // Code5  官方推荐加密算法
//    @Bean("passwordEncoder")
//    public BCryptPasswordEncoder passwordEncoder() {
//        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
//        return bCryptPasswordEncoder;
//    }

//    // Code3----------------------------------------------


        /**
         * 登录成功后跳转
         * 如果需要根据不同的角色做不同的跳转处理,那么继承AuthenticationSuccessHandler重写方法
         *
         * @return
         */
        private SimpleUrlAuthenticationSuccessHandler authenticationSuccessHandler() {
            System.out.println("登录成功后跳转");
            return new SimpleUrlAuthenticationSuccessHandler("/loginSuccess");
        }


        /**
         * Created by ZhenWeiLai on on 2016-10-16.
         */

        private FilterSecurityInterceptor filterSecurityInterceptor(FilterInvocationSecurityMetadataSource securityMetadataSource, AccessDecisionManager accessDecisionManager, AuthenticationManager authenticationManager) {
            FilterSecurityInterceptor filterSecurityInterceptor = new FilterSecurityInterceptor();
            filterSecurityInterceptor.setSecurityMetadataSource(securityMetadataSource);
            filterSecurityInterceptor.setAccessDecisionManager(accessDecisionManager);
            filterSecurityInterceptor.setAuthenticationManager(authenticationManager);
            return filterSecurityInterceptor;
        }


    }




























